#!/usr/bin/python3
# ******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# ******************************************************************************/
import os
import datetime
from retrying import retry
from constant import PROJECT_WORK_DIR, STOP_MAX_ATTEMPT_NUMBER, WAIT_FIXED
from api.gitee import Gitee
from conf import config
from sql import Mysql
from logger import logger
from command import command


class Pull:
    def __init__(self) -> None:
        self._gitee = Gitee()

    def pull_link(self, pr_number, source_repo, target_pr, target_repo):
        """
        pr的关联,相互之间建立关联关系
        :param pr_number: 提交的pr号
        :param target_pr: 待关联的目标pr号
        :return:
                {
                    "link_result":"success/failed",
                    "detail":None
                }
                1. 目标已合入
                2. 目标已关闭
                3. 目标没有依赖关系 *
        """
        pr_info = self._gitee.get_single_pr_info(
            number=target_pr, owner=config.warehouse_owner, repo=target_repo
        )
        if not pr_info:
            logger.warning(f"Target pr info not found: {target_pr}.")
            return dict(link_result="failed", detail="目标PR不存在")
        if pr_info.get("state") == "merged":
            logger.error(f"Target pr has been merged: {target_pr}")
            return dict(link_result="failed", detail="目标PR已合入")
        if pr_info.get("state") == "close":
            logger.error(f"Target pr has been close: {target_pr}")
            return dict(link_result="failed", detail="目标PR关闭")
        # 判断是否有依赖关系

        # 插入数据
        with Mysql() as db:
            sql = """INSERT INTO link_pull(source_pull,link_pull,source_repo,link_repo,link_date,project_name) 
                     VALUES(%s,%s,%s,%s,%s,%s);"""
            if not db.execute(
                sql=sql,
                param=[
                    pr_number,
                    target_pr,
                    source_repo,
                    target_repo,
                    datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    None,
                ],
                commit=True,
            ):
                logger.error(
                    "An error occurred while inserting data into the associated target PR."
                )
                return dict(link_result="failed", detail="目标PR关联错误")
        return dict(link_result="success", detail="目标PR已关联")

    def relation_verify(self, pr_number, repo):
        """
        pr关联关系验证,查询是否有相关联的pr,以及关联pr的状态
        :param pr_number: 待查询的pr
        :return:
            link_pr:[ //关联的pr
                {
                    "status":"merge",
                    "package:"rpmA",
                    "pull":"1"
                }
            ],
            be_link_pr:[ // 被关联的pr
                {
                    "status":"merge",
                    "package:"rpmA",
                    "pull":"1"
                }
            ]
            /None
        """
        relations = dict(link_pr=[], be_link_pr=[])
        with Mysql() as db:
            sql = """SELECT * FROM link_pull WHERE source_pull=%s and source_repo=%s
                     UNION
                    SELECT * FROM link_pull WHERE link_pull=%s and link_repo=%s;"""
            link_prs = db.all(sql, [pr_number, repo, pr_number, repo])
        if not link_prs:
            logger.info(f"There PR is no correlation relationship: {pr_number}")
            return relations

        for pr_link_info in link_prs:
            if (
                pr_link_info["source_pull"] == pr_number
                and pr_link_info["source_repo"] == repo
            ):
                number = pr_link_info["link_pull"]
                repo = pr_link_info["link_repo"]
                link = "link_pr"
            else:
                number = pr_link_info["source_pull"]
                repo = pr_link_info["source_repo"]
                link = "be_link_pr"

            pr_info = self._gitee.get_single_pr_info(
                number=number,
                owner=config.warehouse_owner,
                repo=repo,
            )
            relations[link].append(
                {
                    "status": pr_info.get("state") if pr_info else "unknow",
                    "package": repo,
                    "pull": number,
                }
            )
        return relations

    def del_pull_link(self, pr_number, repo):
        """
        删除pr的关联
        :param pr_number: 关联的pr
        :return:
        True:删除关联成功
                 False:删除关联失败
        """
        with Mysql() as db:
            sql = """SELECT id FROM link_pull WHERE link_pull=%s and link_repo=%s;"""
            if not db.all(sql=sql, params=[pr_number, repo]):
                logger.warning(f"There is no PR correlation:{pr_number}")
                return dict(del_result="failed", detail="PR不存在关联")
            sql = """DELETE FROM link_pull WHERE link_pull=%s and link_repo=%s;"""
            if not db.execute(sql=sql, param=[pr_number, repo], commit=True):

                logger.error(f"Description PR association deletion failed:{pr_number}")

                return dict(del_result="failed", detail="关联关系删除失败")
        return dict(del_result="success", detail="PR关联已清除")

    @retry(
        retry_on_result=lambda result: result is False,
        stop_max_attempt_number=STOP_MAX_ATTEMPT_NUMBER,
        wait_fixed=WAIT_FIXED,
    )
    def fetch_pull(self, url, pr_number, repo, depth=1):
        """
        获取pr,合并至代码仓中打包验证
        :param url: 仓库地址
        :param pr_number: 提交的pr号
        :param depth: 获取的深度
        :return: 获取pr成功或失败
        """
        try:
            os.makedirs(PROJECT_WORK_DIR, exist_ok=True)
        except PermissionError:
            logger.warning(f"Makedirs permission error: {PROJECT_WORK_DIR}")
            return None
        if not os.path.exists(os.path.join(PROJECT_WORK_DIR, repo)):
            command(cmds=["git", "clone", url], cwd=PROJECT_WORK_DIR)
        fetch = f"+refs/pull/{pr_number}/MERGE:refs/pull/{pr_number}/MERGE"
        code, _, error = command(
            ["git", "fetch", "--depth", str(depth), url, fetch],
            cwd=os.path.join(PROJECT_WORK_DIR, repo),
        )
        if code or error:
            logger.error(f"The fetch PR error occurred: {pr_number}")
            return False
        logger.info(f"PR has been successfully merged: {pr_number}")
        command(
            cmds=["git", "checkout", "-f", f"pull/{pr_number}/MERGE"],
            cwd=os.path.join(PROJECT_WORK_DIR, repo),
        )
        return True
